package com.koron.common.core.interceptor;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.koron.bean.annocation.FillField;
import com.koron.bean.enums.OperationMode;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.*;

/**
 * mybatis字段填充器
 * <p>
 * 只能给当前类及其父类的属性填充
 *
 * @author jzp
 * @since 2021/9/26
 */
@Intercepts({
        @Signature(
                type = Executor.class,
                method = "update",
                args = {MappedStatement.class, Object.class}
        )
})
@Slf4j
public class FieldInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取mappedStatement
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        // 获取参数
        Object parameter = invocation.getArgs()[1];

        /*
         *  org.apache.ibatis.binding.MapperMethod.ParamMap
         * 多参数 -> ParamMap
         * Collection -> ParamMap
         * Map -> ParamMap
         * 有@Param -> ParamMap
         */
        if (parameter instanceof Map) {
            // 多参数、批量
            Collection<?> values = ((Map<?, ?>) parameter).values();
            for (Object value : values) {
                // 如果是Collection，里面的每一个元素都fill
                if (value instanceof Collection) {
                    for (Object o : (Collection<?>) value) {
                        fillParameter(mappedStatement, o);
                    }
                } else { // 否则直接fill
                    fillParameter(mappedStatement, value);
                }
            }
        } else if (parameter instanceof Collection) {
            for (Object o : (Collection<?>) parameter) {
                fillParameter(mappedStatement, o);
            }
        } else {
            fillParameter(mappedStatement, parameter);
        }

        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {

    }

    private void fillParameter(MappedStatement mappedStatement, Object parameter) throws IllegalAccessException {
        // 获取 SQL 操作
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();

        if (sqlCommandType.equals(SqlCommandType.INSERT) || sqlCommandType.equals(SqlCommandType.UPDATE)) {
            if (Objects.isNull(parameter)) {
                return;
            }
            // 获取当前参数类
            Class<?> aClass = parameter.getClass();
            String className = aClass.getTypeName();
            // 基本类型直接跳过
            if (ClassUtil.isBasicType(aClass)) {
                return;
            }
            // 获取当前类及其所有父类所有私有成员变量
            Field[] declaredFields = aClass.getDeclaredFields();
            while (aClass.getSuperclass() != null) {
                aClass = aClass.getSuperclass();
                Field[] superField = aClass.getDeclaredFields();
                declaredFields = ArrayUtil.addAll(declaredFields, superField);
            }

            HashSet<String> fields = new HashSet<>();
            for (Field field : declaredFields) {
                Class<?> type = field.getType();
                // 如果字段中有Collection，进去填充，否则如果是原始类，跳过
                if (type.isInstance(Collection.class)) {
                    // 如果是Collection(批量操作)
                    Collection<?> collection = (Collection<?>) field.get(parameter);
                    for (Object o : collection) {
                        fillParameter(mappedStatement, o);
                    }
                } else if (!ClassUtil.isBasicType(type)) {
                    // 重复字段检查（警告类及其父类中不能出现重复字段，可以忽略）
                    if (fields.contains(field.getName())) {
                        log.warn("类{}中的成员变量{}被重复声明", className, field.getName());
                    }
                    fields.add(field.getName());
                    if (field.getAnnotation(FillField.class) != null) {
                        executeFill(sqlCommandType, parameter, field);
                    }
                }

            }
        }

    }

    private void executeFill(SqlCommandType sqlCommandType, Object parameter, Field field) throws IllegalAccessException {
        field.setAccessible(true);
        FillField fillField = field.getAnnotation(FillField.class);
        String fieldName = field.getName();
        if (StrUtil.isNotEmpty(fillField.alias())) {
            fieldName = fillField.alias();
        }
        FieldAdapter fieldAdapter = Adapters.get(fieldName);
        Assert.notNull(fieldAdapter, "字段填充器 " + field.getName() + "Adapter 不存在");

        if (SqlCommandType.INSERT.equals(sqlCommandType)) {
            if (OperationMode.hasInsertMode(fieldAdapter.getOperationMode(), fillField.value())) {
                fieldAdapter.handler(parameter, field);
            }
        } else if (SqlCommandType.UPDATE.equals(sqlCommandType)) {
            if (OperationMode.hasUpdateMode(fieldAdapter.getOperationMode(), fillField.value())) {
                fieldAdapter.handler(parameter, field);
            }
        }
    }

}
